/*
 * Copyright (C) by Argonne National Laboratory
 *     See COPYRIGHT in top-level directory
 */

#include "ad_pvfs2.h"
#include "adio_extern.h"
#include "ad_pvfs2_io.h"
#include "ad_pvfs2_common.h"

void ADIOI_PVFS2_WriteContig(ADIO_File fd, const void *buf, int count,
                             MPI_Datatype datatype, int file_ptr_type,
                             ADIO_Offset offset, ADIO_Status * status, int *error_code)
{
    int ret;
    MPI_Count datatype_size, len;
    PVFS_Request file_req, mem_req;
    PVFS_sysresp_io resp_io;
    ADIOI_PVFS2_fs *pvfs_fs;
    static char myname[] = "ADIOI_PVFS2_WRITECONTIG";

    if (count == 0) {
#ifdef HAVE_STATUS_SET_BYTES
        if (status)
            MPIR_Status_set_bytes(status, datatype, 0);
#endif
        *error_code = MPI_SUCCESS;
        return;
    }

    pvfs_fs = (ADIOI_PVFS2_fs *) fd->fs_ptr;

    MPI_Type_size_x(datatype, &datatype_size);
    len = datatype_size * count;

    ret = PVFS_Request_contiguous(len, PVFS_BYTE, &mem_req);
    /* --BEGIN ERROR HANDLING-- */
    if (ret != 0) {
        *error_code = MPIO_Err_create_code(MPI_SUCCESS,
                                           MPIR_ERR_RECOVERABLE,
                                           myname, __LINE__,
                                           ADIOI_PVFS2_error_convert(ret),
                                           "Error in PVFS_Request_contiguous (memory)", 0);
        return;
    }
    /* --END ERROR HANDLING-- */

    ret = PVFS_Request_contiguous(len, PVFS_BYTE, &file_req);
    /* --BEGIN ERROR HANDLING-- */
    if (ret != 0) {
        *error_code = MPIO_Err_create_code(MPI_SUCCESS,
                                           MPIR_ERR_RECOVERABLE,
                                           myname, __LINE__,
                                           ADIOI_PVFS2_error_convert(ret),
                                           "Error in PVFS_Request_contiguous (file)", 0);
        return;
    }
    /* --END ERROR HANDLING-- */

    if (file_ptr_type == ADIO_EXPLICIT_OFFSET) {
#ifdef ADIOI_MPE_LOGGING
        MPE_Log_event(ADIOI_MPE_write_a, 0, NULL);
#endif
        ret = PVFS_sys_write(pvfs_fs->object_ref, file_req, offset, (void *) buf,
                             mem_req, &(pvfs_fs->credentials), &resp_io);
#ifdef ADIOI_MPE_LOGGING
        MPE_Log_event(ADIOI_MPE_write_b, 0, NULL);
#endif
        /* --BEGIN ERROR HANDLING-- */
        if (ret != 0) {
            *error_code = MPIO_Err_create_code(MPI_SUCCESS,
                                               MPIR_ERR_RECOVERABLE,
                                               myname, __LINE__,
                                               ADIOI_PVFS2_error_convert(ret),
                                               "Error in PVFS_sys_write", 0);
            goto fn_exit;
        }
        /* --END ERROR HANDLING-- */

        fd->fp_sys_posn = offset + (int) resp_io.total_completed;
    } else {
#ifdef ADIOI_MPE_LOGGING
        MPE_Log_event(ADIOI_MPE_write_a, 0, NULL);
#endif
        ret = PVFS_sys_write(pvfs_fs->object_ref, file_req, fd->fp_ind, (void *) buf,
                             mem_req, &(pvfs_fs->credentials), &resp_io);
#ifdef ADIOI_MPE_LOGGING
        MPE_Log_event(ADIOI_MPE_write_b, 0, NULL);
#endif
        /* --BEGIN ERROR HANDLING-- */
        if (ret != 0) {
            *error_code = MPIO_Err_create_code(MPI_SUCCESS,
                                               MPIR_ERR_RECOVERABLE,
                                               myname, __LINE__,
                                               ADIOI_PVFS2_error_convert(ret),
                                               "Error in PVFS_sys_write", 0);
            goto fn_exit;
        }
        /* --END ERROR HANDLING-- */
        fd->fp_ind += (int) resp_io.total_completed;
        fd->fp_sys_posn = fd->fp_ind;
    }
#ifdef HAVE_STATUS_SET_BYTES
    if (status)
        MPIR_Status_set_bytes(status, datatype, resp_io.total_completed);
#endif
    *error_code = MPI_SUCCESS;
  fn_exit:
    PVFS_Request_free(&file_req);
    PVFS_Request_free(&mem_req);
    return;
}

int ADIOI_PVFS2_WriteStridedListIO(ADIO_File fd, const void *buf, int count,
                                   MPI_Datatype datatype, int file_ptr_type,
                                   ADIO_Offset offset, ADIO_Status * status, int *error_code)
{
    return ADIOI_PVFS2_StridedListIO(fd, (void *) buf, count,
                                     datatype, file_ptr_type, offset, status, error_code, WRITE);
}

int ADIOI_PVFS2_WriteStridedDtypeIO(ADIO_File fd, const void *buf, int count,
                                    MPI_Datatype datatype, int file_ptr_type,
                                    ADIO_Offset offset, ADIO_Status * status, int *error_code)
{
    return ADIOI_PVFS2_StridedDtypeIO(fd, (void *) buf, count,
                                      datatype, file_ptr_type, offset, status, error_code, WRITE);
}


void ADIOI_PVFS2_WriteStrided(ADIO_File fd, const void *buf, int count,
                              MPI_Datatype datatype, int file_ptr_type,
                              ADIO_Offset offset, ADIO_Status * status, int *error_code)
{
    /* four ways (to date) that we can carry out strided i/o accesses:
     * - naive posix
     * - 'true' Datatype (from avery)
     * - new List I/O (from avery)
     * - classic List I/O  (the one that's always been in ROMIO)
     * I imagine we'll keep Datatype as an optional optimization, and after a
     * release or two promote it to the default
     */

    /* a lot of near-duplication from ADIOI_PVFS2_ReadStrided: for
     * debugging/testing it's helpful to be able to turn on and off these
     * optimizations separately for the read and write cases */
    int ret = -1;
    if (fd->hints->fs_hints.pvfs2.posix_write == ADIOI_HINT_ENABLE) {
        ADIOI_GEN_WriteStrided_naive(fd, buf, count,
                                     datatype, file_ptr_type, offset, status, error_code);
        return;
    }
    if (fd->hints->fs_hints.pvfs2.dtype_write == ADIOI_HINT_ENABLE) {
        ret = ADIOI_PVFS2_WriteStridedDtypeIO(fd, buf, count,
                                              datatype, file_ptr_type, offset, status, error_code);

        /* Fall back to list I/O if datatype I/O didn't work */
        if (ret != 0) {
            fprintf(stderr, "Falling back to list I/O since datatype I/O failed\n");
            ret = ADIOI_PVFS2_WriteStridedListIO(fd, buf, count,
                                                 datatype, file_ptr_type,
                                                 offset, status, error_code);
        }
        return;
    }
    /* Use list I/O in the base case */
    if (fd->hints->fs_hints.pvfs2.listio_write == ADIOI_HINT_ENABLE) {
        ret = ADIOI_PVFS2_WriteStridedListIO(fd, buf, count, datatype,
                                             file_ptr_type, offset, status, error_code);
        return;
    }

    /* Use classic list I/O if no hints given base case */
    ADIOI_PVFS2_OldWriteStrided(fd, buf, count, datatype,
                                file_ptr_type, offset, status, error_code);
    return;
}
